package cn.demoncat.util.io;

import cn.demoncat.util.exception.IoRuntimeException;
import cn.demoncat.util.lang.JsonUtil;
import cn.demoncat.util.lang.NumberUtil;
import cn.demoncat.util.lang.constant.CharsetConstant;
import cn.demoncat.util.lang.constant.DateConstant;
import cn.demoncat.util.lang.constant.StringConstant;
import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * FreeMarker工具
 *
 * 在线编辑器：https://www.renfei.net/kitbox/freemarkerTest
 * 
 * @author 延晓磊
 *
 * @since 2019年7月16日
 */
public class FreeMarkerUtil {

	/**
	 * 渲染模板
	 * 
	 * @param content	模板内容
	 * @param data		模板数据（数值按#.##格式化，其它格式需预转换为String）
	 * 
	 * @return 渲染结果
	 * 
	 * @author 延晓磊
	 * 
	 *
	 * @since 2019年6月20日
	 */
	public static String out(String content, Object data) {
		try {
			// 创建Freemarker配置
			Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
			// 设置全局的字符编码、数字转换格式、日期转换格式
			cfg.setDefaultEncoding(CharsetConstant.UTF8);
			cfg.setNumberFormat(NumberUtil.Format.FLOAT);
			cfg.setDateFormat(DateConstant.DATE_PATTERN);
			cfg.setDateTimeFormat(DateConstant.DATE_TIME_PATTERN);
			// 加载模板
			Template temp = new Template(null, content, cfg);
			// 合并模板和数据，通过指定的Write流输出
			StringWriter out = new StringWriter();
			temp.process(data, out);
			return out.toString();
		} catch (Exception e) {
			throw new IoRuntimeException("渲染模板失败：\r\ncontent = " + content +"\r\ndata = " + JsonUtil.toStr(data), e);
		}
	}
	
	/**
	 * 渲染模板
	 * 
	 * @param templateDir	模板目录：项目路径 = 以/开头，不以/webapps开头；磁盘路径 = 以/webapps开头，或不以/开头
	 * @param templateFile	模板文件：相对于模板目录，通常以.ftl后缀，如tmp.ftl
	 * @param data			数据（数值按#.##格式化，其它格式需预转换为String）
	 * 
	 * @return 渲染结果
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年6月20日
	 */
	public static String out(String templateDir, String templateFile, Object data) {
		return out(templateDir, templateFile, data, new StringWriter()).toString();
	}
	
	/**
	 * 渲染并输出模板
	 * 
	 * @param templateDir	模板目录：项目路径 = 以/开头，不以/webapps开头；磁盘路径 = 以/webapps开头，或不以/开头
	 * @param templateFile	模板文件：相对于模板目录，通常以.ftl后缀，如tmp.ftl
	 * @param data			数据（数值按#.##格式化，其它格式需预转换为String）
	 * @param out			输出流，完成后将关闭
	 * 
	 * @return out 输出流：已输出并关闭，但StringWriter等非IO流可以读取结果
	 * 
	 * @author 延晓磊
	 * 
	 *
	 * @since 2019年6月20日
	 */
	public static OutputStream out(String templateDir, String templateFile, Object data, OutputStream out) {
        out(templateDir, templateFile, data, new OutputStreamWriter(out, StandardCharsets.UTF_8));
        return out;
	}
	
	/**
	 * 渲染并输出模板
	 * 
	 * @param templateDir	模板目录：项目路径 = 以/开头，不以/webapps开头；磁盘路径 = 以/webapps开头，或不以/开头
	 * @param templateFile	模板文件：相对于模板目录，通常以.ftl后缀，如tmp.ftl
	 * @param data			数据（数值按#.##格式化，其它格式需预转换为String）
	 * @param out			输出流，完成后将关闭
	 * 
	 * @return out 输出流：已输出并关闭，但StringWriter等非IO流可以读取结果
	 * 
	 * @author 延晓磊
	 * 
	 *
	 * @since 2019年6月20日
	 */
	public static Writer out(String templateDir, String templateFile, Object data, Writer out) {
		try {
			// 创建Freemarker配置
			Configuration cfg = new Configuration(Configuration.getVersion());
			// 指定模板文件的目录
			if (templateDir.startsWith(StringConstant.SEPARATOR) && !templateDir.startsWith("/webapps")) {
				// 基于项目的绝对路径，如src/main/resource/ftl/dir = /ftl/dir
				cfg.setClassForTemplateLoading(FreeMarkerUtil.class, templateDir);
			} else {
				// 基于磁盘的绝对路径，如 E://ftl/dir 或 /webapps/demoncat-bus-web/ftl/dir
				cfg.setDirectoryForTemplateLoading(new File(templateDir));
			}
			// 设置全局的字符编码、数字转换格式、日期转换格式
			cfg.setDefaultEncoding(CharsetConstant.UTF8);
			cfg.setNumberFormat(NumberUtil.Format.FLOAT);
			cfg.setDateFormat(DateConstant.DATE_PATTERN);
			cfg.setDateTimeFormat(DateConstant.DATE_TIME_PATTERN);
			// 加载模板文件(相对于模板目录，通常以.ftl后缀，如tmp.ftl)
			Template temp = cfg.getTemplate(templateFile);
			// 合并模板和数据，通过指定的Write流输出
			temp.process(data, out);
			out.flush();
			return out;
		} catch (Exception e) {
			throw new IoRuntimeException("渲染模板失败：dir = " + templateDir +" , template = " + templateFile + "\r\ndata = " + JsonUtil.toStr(data), e);
		}finally {
			CloseUtil.close(out);
		}
	}
	
	
}
